/*
 * Created on 2008-7-30
 *
 * TODO
 */
package jacky.lanlan.song.junitx.v3;

import junit.extensions.TestDecorator;
import junit.framework.*;

import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;

/**
 * 使用包装器模式的Runner。
 * <p>
 * 任何使用该Runner的测试用例，都必须标注 <code>Wrapper</code> ，以提供自定义的包装器。<br/>
 * WrapperRunner 会使用所提供的包装器，包装原始测试用例，然后运行。这样，就可以通过包装器透明的增加
 * 测试行为（比如耗时测试，并发等）。
 * @author Jacky.Song
 */
public class WrapperRunner extends Runner {
	private Test wrappedTest;
	
	public WrapperRunner(Class<?> klass) {
		super();
		
		Wrapper wrapper = klass.getAnnotation(Wrapper.class);
		jacky.lanlan.song.test.common.Assert.notNull(wrapper, "用 "+this.getClass().getSimpleName()+" 运行的测试必须标注 Wrapper");
		try {
			Test test = new TestSuite(klass.asSubclass(TestCase.class)); // 通过创建Suite，JUnit会自动使用反射查找所有以"test"打头的测试方法，否则就需要自己做
			UnitWrapper tw = wrapper.value().newInstance();
			wrappedTest = tw.wrapTest(test); //包装原始测试类
		}
		catch (Exception e) {
			throw new RuntimeException("创建测试包装器时出错："+e);
		}
		
	}

	/**
	 * 适配 Junit3 的TestListener的适配器，这个适配器把TestResult的事件转发给RunNotifier，
	 * 这样就可以翻译JUnit 3的事件到JUnit 4。
	 * @author Jacky.Song
	 */
	private class TestListenerAdapter implements TestListener{
		private RunNotifier notifier;
		
		public TestListenerAdapter (RunNotifier notifier) {
			this.notifier=notifier;
		}

		public void endTest(Test test) {
			notifier.fireTestFinished(makeDescription(test));
		}

		public void startTest(Test test) {
			notifier.fireTestStarted(makeDescription(test));
		}

		// Implement junit.framework.TestListener
		public void addError(Test test, Throwable t) {
			Failure failure= new Failure(makeDescription(test), t);
			notifier.fireTestFailure(failure);
		}
		
		public void addFailure(Test test, AssertionFailedError t) {
			addError(test, t);
		}

	}
	
	@Override
	public void run(RunNotifier notifier) {
		TestResult result= new TestResult();
		result.addListener(new TestListenerAdapter(notifier));
		wrappedTest.run(result);// 运行包装器，透明的改变测试行为。
	}
	
	@Override
	public Description getDescription() {
		return this.makeDescription(wrappedTest);
	}

	//// COPYED FROM JUnit38ClassRunner ////
	
	private Description makeDescription(Test test) {
		if (test instanceof TestCase) {
			TestCase tc= (TestCase) test;
			return Description.createTestDescription(tc.getClass(), tc.getName());
		} else if (test instanceof TestSuite) {
			TestSuite ts= (TestSuite) test;
			String name= ts.getName() == null ? "" : ts.getName();
			Description description= Description.createSuiteDescription(name);
			int n= ts.testCount();
			for (int i= 0; i < n; i++)
				description.addChild(makeDescription(ts.testAt(i)));
			return description;
		} else if (test instanceof JUnit4TestAdapter) {
			JUnit4TestAdapter adapter= (JUnit4TestAdapter) test;
			return adapter.getDescription();
		} else if (test instanceof TestDecorator) {
			TestDecorator decorator= (TestDecorator) test;
			return makeDescription(decorator.getTest());
		} else {
			// This is the best we can do in this case
			return Description.createSuiteDescription(test.getClass());
		}
	}

}
